package com.ffcs.crmd.tsp.server.jdbc.service.impl;

/**
 * 
 * 功能说明:获取主键服务
 *
 * @author ZHONGFUHUA
 * 
 * @Date 2016年4月10日 下午2:30:45
 *
 *
 * 版本号  |   作者   |  修改时间   |   修改内容
 *
 */
public class TspSeqServiceImpl {
    
    private long                           sequence           = 0L;                          // 起始值
    private long                           twepoch            = 1288834974657L;              // 起始时间戳
    private long                           sequenceBits       = 12L;                         // 序列号12位
    private long                           timestampLeftShift = sequenceBits;                // 时间毫秒数左移22位
    private long                           sequenceMask       = -1L ^ (-1L << sequenceBits); // 4095
    private long                           lastTimestamp      = -1L;
    
    private static final TspSeqServiceImpl instance           = new TspSeqServiceImpl();
    
    protected TspSeqServiceImpl() {
        
    }
    
    private synchronized long nextId() {
        long timestamp = timeGen();
        // 如果服务器时间有问题(时钟后退)报错。
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        // 如果上次生成时间和当前时间相同,在同一毫秒内
        if (lastTimestamp == timestamp) {
            // sequence自增，因为sequence只有12bit，所以和sequenceMask相与一下，去掉高位
            sequence = (sequence + 1) & sequenceMask;
            // 判断是否溢出,也就是每毫秒内超过4095，当为4096时，与sequenceMask相与，sequence就等于0
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp); // 自旋等待到下一毫秒
            }
        } else {
            sequence = 0L; // 如果和上次生成时间不同,重置sequence，就是下一毫秒开始，sequence计数重新从0开始累加
        }
        lastTimestamp = timestamp;
        // 最后按照规则拼出ID。
        // 0000000000000000000000000000000000000 000000000000
        // time                                  sequence
        return ((((timestamp - twepoch) << 15) >> 15) << timestampLeftShift) | sequence;
    }
    
    /**
     * 
     * 功能说明:
     * 
     * @param lastTimestamp
     * @return
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    
    /**
     * 
     * 功能说明:
     * 
     * @return
     */
    private long timeGen() {
        return System.currentTimeMillis();
    }
    
    /**
     * 
     * 功能说明:获取主键
     * 
     * @author ZHONGFUHUA
     *
     * @Date 2016年4月10日
     *
     */
    public static long getSeq() {
        return instance.nextId();
    }
    
    public static void main(String[] args) {
        System.out.println(TspSeqServiceImpl.getSeq());
    }
}
